﻿// MIT License Copyright 2014 (c) David Melendez. All rights reserved. See License.txt in the project root for license information.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ElCamino.AspNet.Identity.AzureTable;
using Microsoft.AspNet.Identity;
using System.Collections.Generic;
using System.Security.Claims;
using System.Linq;
using ElCamino.AspNet.Identity.AzureTable.Model;

namespace ElCamino.AspNet.Identity.AzureTable.Tests
{
    [TestClass]
    public partial class UserStoreTests
    {
        #region Static and Const Members
        public static string DefaultUserPassword;

        private static List<IdentityRole> RoleCollection = new List<IdentityRole>(10);
        private static List<Claim> ClaimCollection = new List<Claim>(10);

        private static IdentityUser User = null;

        #endregion

        #region Test Initialization
        [TestInitialize]
        public void Initialize()
        {
            DefaultUserPassword = Guid.NewGuid().ToString();
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                var taskCreateTables = store.CreateTablesIfNotExists();
                taskCreateTables.Wait();
            }

            CreateUser();

            using (RoleStore<IdentityRole> rstore = new RoleStore<IdentityRole>())
            {
                string strAdminRole = string.Format("{0}_{1}", Constants.AccountRoles.AccountTestAdminRole, Guid.NewGuid().ToString("N"));
                var taskAdmin = rstore.CreateAsync(new IdentityRole(strAdminRole));
                taskAdmin.Wait();
                var adminRole = rstore.FindByNameAsync(strAdminRole);
                adminRole.Wait();
                RoleCollection.Add(adminRole.Result);

                string strUserRole = string.Format("{0}_{1}", Constants.AccountRoles.AccountTestUserRole, Guid.NewGuid().ToString("N"));
                var taskUser = rstore.CreateAsync(new IdentityRole(strUserRole));
                taskUser.Wait();
                var userRole = rstore.FindByNameAsync(strUserRole);
                userRole.Wait();
                RoleCollection.Add(userRole.Result);

                strUserRole = string.Format("{0}_{1}", Constants.AccountRoles.AccountTestUserRole, Guid.NewGuid().ToString("N"));
                var taskUser2 = rstore.CreateAsync(new IdentityRole(strUserRole));
                taskUser2.Wait();
                var userRole2 = rstore.FindByNameAsync(strUserRole);
                userRole2.Wait();
                RoleCollection.Add(userRole2.Result);

            }

            ClaimCollection.Add(new Claim(Constants.AccountClaimTypes.AccountTestAdminClaim, Guid.NewGuid().ToString()));
            ClaimCollection.Add(new Claim(Constants.AccountClaimTypes.AccountTestUserClaim, Guid.NewGuid().ToString()));

        }
        #endregion

        private UserLoginInfo GenGoogleLogin()
        {
           return new UserLoginInfo(Constants.LoginProviders.GoogleProvider.LoginProvider,
                        Constants.LoginProviders.GoogleProvider.ProviderKey);
        }

        private IdentityUser GenTestUser()
        {
            Guid id = Guid.NewGuid();
            IdentityUser user = new IdentityUser()
            {
                Email = id.ToString() + "@live.com",
                UserName = id.ToString("N"),
                LockoutEnabled = false,
                LockoutEndDateUtc = null,
                PhoneNumber = "555-555-5555",
                TwoFactorEnabled = false,
            };

            return user;
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void UserStoreCtors()
        {
            try
            {
                new UserStore<IdentityUser>(null);
            }
            catch (ArgumentException) { }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void CreateUser()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = GenTestUser();
                    var taskUser = manager.CreateAsync(user, DefaultUserPassword);
                    taskUser.Wait();
                    Assert.IsTrue(taskUser.Result.Succeeded, string.Concat(taskUser.Result.Errors));
                    User = user;

                    try
                    {
                        var task = store.CreateAsync(null);
                        task.Wait();
                    }
                    catch (AggregateException agg) 
                    {
                        agg.ValidateAggregateException<ArgumentException>();
                    }


                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void DeleteUser()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = GenTestUser();
                    var taskUser = manager.CreateAsync(user, DefaultUserPassword);
                    taskUser.Wait();
                    Assert.IsTrue(taskUser.Result.Succeeded, string.Concat(taskUser.Result.Errors));

                    AddUserClaimHelper(user);
                    AddUserLoginHelper(user);
                    AddUserRoleHelper(user);

                    var findUserTask2 = manager.FindByIdAsync(user.Id);
                    findUserTask2.Wait();
                    user = findUserTask2.Result;

                    var taskUserDel = manager.DeleteAsync(user);
                    taskUserDel.Wait();
                    Assert.IsTrue(taskUserDel.Result.Succeeded, string.Concat(taskUser.Result.Errors));

                    var findUserTask = manager.FindByIdAsync(user.Id);
                    findUserTask.Wait();
                    Assert.IsNull(findUserTask.Result, "Found user Id, user not deleted.");

                    try
                    {
                        var task = store.DeleteAsync(null);
                        task.Wait();
                    }
                    catch (AggregateException agg)
                    {
                        agg.ValidateAggregateException<ArgumentException>();
                    }
                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void UpdateUser()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = GenTestUser();
                    var taskUser = manager.CreateAsync(user, DefaultUserPassword);
                    taskUser.Wait();
                    Assert.IsTrue(taskUser.Result.Succeeded, string.Concat(taskUser.Result.Errors));

                    var taskUserUpdate = manager.UpdateAsync(user);
                    taskUserUpdate.Wait();
                    Assert.IsTrue(taskUserUpdate.Result.Succeeded, string.Concat(taskUserUpdate.Result.Errors));

                    try
                    {
                        store.UpdateAsync(null);
                    }
                    catch (ArgumentException) { }
                }
            }
        }



        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void FindUserByEmail()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = User;
                    var findUserTask = manager.FindByEmailAsync(user.Email);
                    findUserTask.Wait();
                    Assert.AreEqual<string>(user.Email, findUserTask.Result.Email, "Found user email not equal");
                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void FindUserById()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = User;
                    var findUserTask = manager.FindByIdAsync(user.Id);
                    findUserTask.Wait();
                    Assert.AreEqual<string>(user.Id, findUserTask.Result.Id, "Found user Id not equal");
                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void FindUserByName()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = User;
                    var findUserTask = manager.FindByNameAsync(user.UserName);
                    findUserTask.Wait();
                    Assert.AreEqual<string>(user.UserName, findUserTask.Result.UserName, "Found user UserName not equal");
                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void AddUserLogin()
        {
            AddUserLoginHelper(User);
        }

        public void AddUserLoginHelper(IdentityUser user)
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var loginInfo = GenGoogleLogin();
                    var userAddLoginTask = manager.AddLoginAsync(user.Id, loginInfo);
                    userAddLoginTask.Wait();
                    Assert.IsTrue(userAddLoginTask.Result.Succeeded, string.Concat(userAddLoginTask.Result.Errors));

                    var loginGetTask = manager.GetLoginsAsync(user.Id);
                    loginGetTask.Wait();
                    Assert.IsTrue(loginGetTask.Result
                        .Any(log => log.LoginProvider == loginInfo.LoginProvider
                            & log.ProviderKey == loginInfo.ProviderKey), "LoginInfo not found: GetLoginsAsync");

                    var loginGetTask2 = manager.FindAsync(loginGetTask.Result.First());
                    loginGetTask2.Wait();
                    Assert.IsNotNull(loginGetTask2.Result, "LoginInfo not found: FindAsync");

                }
            }
        }


        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void AddRemoveUserLogin()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = GenTestUser();
                    var taskUser = manager.CreateAsync(user, DefaultUserPassword);
                    taskUser.Wait();
                    Assert.IsTrue(taskUser.Result.Succeeded, string.Concat(taskUser.Result.Errors));

                    var loginInfo = GenGoogleLogin();
                    var userAddLoginTask = manager.AddLoginAsync(user.Id, loginInfo);
                    userAddLoginTask.Wait();
                    Assert.IsTrue(userAddLoginTask.Result.Succeeded, string.Concat(userAddLoginTask.Result.Errors));

                    var loginGetTask = manager.GetLoginsAsync(user.Id);
                    loginGetTask.Wait();
                    Assert.IsTrue(loginGetTask.Result
                        .Any(log=> log.LoginProvider == loginInfo.LoginProvider
                            & log.ProviderKey == loginInfo.ProviderKey), "LoginInfo not found: GetLoginsAsync");

                    var loginGetTask2 = manager.FindAsync(loginGetTask.Result.First());
                    loginGetTask2.Wait();
                    Assert.IsNotNull(loginGetTask2.Result, "LoginInfo not found: FindAsync");

                    var userRemoveLoginTaskNeg1 = manager.RemoveLoginAsync(user.Id, new UserLoginInfo(string.Empty, loginInfo.ProviderKey));
                    userRemoveLoginTaskNeg1.Wait();

                    var userRemoveLoginTaskNeg2 = manager.RemoveLoginAsync(user.Id, new UserLoginInfo(loginInfo.LoginProvider, string.Empty));
                    userRemoveLoginTaskNeg2.Wait();

                    var userRemoveLoginTask = manager.RemoveLoginAsync(user.Id, loginInfo);
                    userRemoveLoginTask.Wait();
                    Assert.IsTrue(userRemoveLoginTask.Result.Succeeded, string.Concat(userRemoveLoginTask.Result.Errors));
                    var loginGetTask3 = manager.GetLoginsAsync(user.Id);
                    loginGetTask3.Wait();
                    Assert.IsTrue(!loginGetTask3.Result.Any(), "LoginInfo not removed");

                    //Negative cases

                    var loginFindNeg = manager.FindAsync(new UserLoginInfo("asdfasdf", "http://4343443dfaksjfaf"));
                    loginFindNeg.Wait();
                    Assert.IsNull(loginFindNeg.Result, "LoginInfo found: FindAsync");

                    try
                    {
                        store.AddLoginAsync(null, loginInfo);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.AddLoginAsync(user, null);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveLoginAsync(null, loginInfo);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveLoginAsync(user, null);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.FindAsync(null);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.GetLoginsAsync(null);
                    }
                    catch (ArgumentException) { }
                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void AddUserRole()
        {
            AddUserRoleHelper(User);
        }

        public void AddUserRoleHelper(IdentityUser user)
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    string roleName = RoleCollection[1].Name;
                    var userRoleTask = manager.AddToRoleAsync(user.Id, roleName);
                    userRoleTask.Wait();
                    Assert.IsTrue(userRoleTask.Result.Succeeded, string.Concat(userRoleTask.Result.Errors));

                    var roles2Task = manager.IsInRoleAsync(user.Id, roleName);
                    roles2Task.Wait();
                    Assert.IsTrue(roles2Task.Result, "Role not found");

                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void AddRemoveUserRole()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = User;
                    string roleName = RoleCollection[1].Name;
                    var userRoleTask = manager.AddToRoleAsync(user.Id, roleName);
                    userRoleTask.Wait();
                    Assert.IsTrue(userRoleTask.Result.Succeeded, string.Concat(userRoleTask.Result.Errors));

                    var rolesTask = manager.GetRolesAsync(user.Id);
                    rolesTask.Wait();
                    Assert.IsTrue(rolesTask.Result.Contains(roleName), "Role not found");

                    var roles2Task = manager.IsInRoleAsync(user.Id, roleName);
                    roles2Task.Wait();
                    Assert.IsTrue(roles2Task.Result, "Role not found");

                    var userRemoveTask = manager.RemoveFromRoleAsync(user.Id, roleName);
                    userRemoveTask.Wait();
                    var rolesTask2 = manager.GetRolesAsync(user.Id);
                    rolesTask2.Wait();
                    Assert.IsFalse(rolesTask2.Result.Contains(roleName), "Role not removed.");

                    try
                    {
                        store.AddToRoleAsync(null, roleName);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.AddToRoleAsync(user, null);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.AddToRoleAsync(user, Guid.NewGuid().ToString());
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveFromRoleAsync(null, roleName);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveFromRoleAsync(user, null);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.GetRolesAsync(null);
                    }
                    catch (ArgumentException) { }

                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void IsUserInRole()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>())
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = User;
                    string roleName = RoleCollection[2].Name;
                    var userRoleTask = manager.AddToRoleAsync(user.Id, roleName);
                    userRoleTask.Wait();
                    Assert.IsTrue(userRoleTask.Result.Succeeded, string.Concat(userRoleTask.Result.Errors));

                    var roles2Task = manager.IsInRoleAsync(user.Id, roleName);
                    roles2Task.Wait();
                    Assert.IsTrue(roles2Task.Result, "Role not found");

                   
                    try
                    {
                        store.IsInRoleAsync(null, roleName);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.IsInRoleAsync(user, null);
                    }
                    catch (ArgumentException) { }
                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void AddUserClaim()
        {
            AddUserClaimHelper(User);
        }

        private void AddUserClaimHelper(IdentityUser user)
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>(new IdentityCloudContext<IdentityUser>()))
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    Claim claim = ClaimCollection[0];
                    var userClaimTask = manager.AddClaimAsync(user.Id, claim);
                    userClaimTask.Wait();
                    Assert.IsTrue(userClaimTask.Result.Succeeded, string.Concat(userClaimTask.Result.Errors));
                    var claimsTask = manager.GetClaimsAsync(user.Id);
                    claimsTask.Wait();
                    Assert.IsTrue(claimsTask.Result.Any(c => c.Value == claim.Value & c.ValueType == claim.ValueType), "Claim not found");
                }
            }

        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void AddRemoveUserClaim()
        {
            using (UserStore<IdentityUser> store = new UserStore<IdentityUser>(new IdentityCloudContext<IdentityUser>()))
            {
                using (UserManager<IdentityUser> manager = new UserManager<IdentityUser>(store))
                {
                    var user = User;
                    Claim claim = ClaimCollection[1];
                    var userClaimTask = manager.AddClaimAsync(user.Id, claim);
                    userClaimTask.Wait();
                    Assert.IsTrue(userClaimTask.Result.Succeeded, string.Concat(userClaimTask.Result.Errors));
                    var claimsTask = manager.GetClaimsAsync(user.Id);
                    claimsTask.Wait();
                    Assert.IsTrue(claimsTask.Result.Any(c => c.Value == claim.Value & c.ValueType == claim.ValueType), "Claim not found");


                    var userRemoveClaimTask = manager.RemoveClaimAsync(user.Id, claim);
                    userRemoveClaimTask.Wait();
                    Assert.IsTrue(userClaimTask.Result.Succeeded, string.Concat(userClaimTask.Result.Errors));
                    var claimsTask2 = manager.GetClaimsAsync(user.Id);
                    claimsTask2.Wait();
                    Assert.IsTrue(!claimsTask2.Result.Any(), "Claim not removed");

                    try
                    {
                        var task = store.AddClaimAsync(null, claim);
                        task.Wait();
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.AddClaimAsync(user, null);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveClaimAsync(null, claim);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveClaimAsync(user, null);
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveClaimAsync(user, new Claim(string.Empty, Guid.NewGuid().ToString()));
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.RemoveClaimAsync(user, new Claim(claim.Type, string.Empty));
                    }
                    catch (ArgumentException) { }

                    try
                    {
                        store.GetClaimsAsync(null);
                    }
                    catch (ArgumentException) { }
                }
            }
        }

        [TestMethod]
        [TestCategory("Identity.Azure.UserStore")]
        public void ThrowIfDisposed()
        {
            UserStore<IdentityUser> store = new UserStore<IdentityUser>();
            store.Dispose();
            GC.Collect();
            try
            {
                var task = store.DeleteAsync(null);
            }
            catch (AggregateException agg)
            {
                agg.ValidateAggregateException<ArgumentException>();
            }
        }

    }
}
